import { useEffect, useState, useRef, useImperativeHandle, forwardRef } from 'react';
import { message, Spin } from 'antd';
import { CloseOutlined } from '@ant-design/icons';
import camundaModdleDescriptor from 'camunda-bpmn-moddle/resources/camunda.json';
import { observer } from '@formily/react';
import { useApp } from '@inbiz/render';
import { uid } from '@inbiz/shared';
import {
  xmlStr,
  startEventId,
  firstTaskId,
  customModdleActivitiPackage,
  canOperate1,
  defaultUserTask,
  defaultProcessDefinition,
  defaultNewUserTask,
  defaultCtSignTask,
  defaultSubProcess,
  getProcessIdFromSearch,
  getInitUserTask,
} from './utils';
import CustomModeler from './customModeler';
import CustomPropertiesPanel from './customPropertiesPanel';
import SetVariableDrawer from './modalElements/SetVariableDrawer';
import VersionManagement from './modalElements/VersionManagement';
import ToolBar from './toolBar';
import SourceCode from './sourceCode';
import Header from './header';
import CustomPalette from './customPalette';
import { getProcessData } from './server';
import _ from 'lodash';
import './style.less';

interface bpmnProps {
  ref?: any;
}

const Bpmn = observer((props: bpmnProps) => {
  const { ref } = props;
  const appInfo = useApp();
  const [requestFailed, setRequestFailed] = useState(false);
  const [customModeler, setCustomModeler] = useState<any>(null);
  const [current, setCurrent] = useState('shape');
  const [curElement, setCurElement] = useState<any[]>([]);
  const [bpmnXML, setBpmnXML] = useState(xmlStr);
  const [loading, setLoading] = useState(false);
  const [processData, setProcessData] = useState<any>(null);
  const [propertiesPanelVisiable, setPropertiesPanelVisiable] = useState(false);
  const [variableDrawerVisiable, setVariableDrawerVisiable] = useState(false);
  const [versionVisiable, setVersionVisiable] = useState(false);
  const [afterInit, setAfterInit] = useState(false);
  const [searchProcessId] = useState(getProcessIdFromSearch('processid'));
  // const headerRef = useRef<any>();
  const customPropertiesPanelRef = useRef<any>();
  const customPaletteRef = useRef<any>();

  const initBpmn = async (processData: any) => {
    if (customModeler) {
      customModeler.destroy();
      setCustomModeler(null);
      window['inbizBpmn'] = null;
    };
    // @ts-ignore
    const bpmnModeler: any = new CustomModeler({
      container: '#bpmnCanvas',
      height: '100%',
      additionalModules: [
        {
          zoomScroll: ['value', ''],
          autoScroll: ['value', ''],
          labelEditingProvider: ['value', ''],
          paletteProvider: ['value', ''],
          alignElementsContextPadProvider: ['type', () => { }],
        },
      ],
      moddleExtensions: {
        camunda: camundaModdleDescriptor,
      },
      bpmnRenderer: {
        defaultStrokeColor: '#bbb',
      },
      keyboard: {
        bindTo: document,
      },
    });
    const Canvas = bpmnModeler.get('canvas');
    const Moddle = bpmnModeler.get('moddle');
    Canvas.zoom('fit-viewport');
    Moddle.registry.registerPackage(customModdleActivitiPackage);
    const Modeling = bpmnModeler.get('modeling');
    Modeling.setPropertiesPanelVisiable = setPropertiesPanelVisiable;
    const _userTask = processData?.extPropJson ? getInitUserTask(processData) : defaultUserTask;
    const _callActivityTask = processData?.extPropJson ? JSON.parse(processData?.extPropJson)?.callActivityTask : [];
    const CustomRenderer = bpmnModeler.get('customRenderer');
    CustomRenderer.shearCache = [];
    CustomRenderer.bpmnForView = false;
    CustomRenderer.activities = [];
    CustomRenderer.currentNodeId = '';
    const _sequenceFlow = processData?.extPropJson ? JSON.parse(processData?.extPropJson)?.sequenceFlow : []
    bpmnModeler['processData'] = processData?.extPropJson ? JSON.parse(processData?.extPropJson) : {
      processDefinition: defaultProcessDefinition(processData),
      userTask: _userTask,
      callActivityTask: _callActivityTask,
      sequenceFlow: _sequenceFlow,
    };
    bpmnModeler['currentView'] = 'shape';
    window['inbizBpmn'] = bpmnModeler;
    setCustomModeler(bpmnModeler);
    customPropertiesPanelRef?.current?.setProcessDefinition(defaultProcessDefinition(processData));
    customPropertiesPanelRef?.current?.setSequenceFlowData(_sequenceFlow);
    customPropertiesPanelRef?.current?.setSubProcessData(_callActivityTask);
    customPropertiesPanelRef?.current?.setUserTaskData(_userTask);
    bpmnModeler.toggleXml = () => {
      setCurrent((pre: string) => {
        if (pre == 'shape') {
          window.inbizBpmn.currentView = 'code';
          return 'code';
        } else {
          window.inbizBpmn.currentView = 'shape';
          return 'shape';
        };
      });
      return window.inbizBpmn.currentView == 'code' ? 'shape' : 'code';
    };

    try {
      const result = await bpmnModeler.importXML(processData?.processDefinitionXml ?? xmlStr);
    } catch (error) {
      console.log(error);
    };
  };

  const getProcessInfo = async (id: string) => {
    setLoading(true);
    getProcessData(id).then(async (res) => {
      if (res?.code == '200') {
        setProcessData(res?.details);
        setBpmnXML(res?.details?.processDefinitionXml);
        await initBpmn(res?.details);
      } else {
        setRequestFailed(true);
        message.error(res?.message || '流程查询失败');
      };
      setLoading(false);
    }).catch((error) => {
      message.error(error || '流程查询失败');
      setLoading(false);
      setRequestFailed(true);
    });
  };

  const saveDiagram = async () => {
    if (customModeler) {
      try {
        const _xml = await customModeler.saveXML({ format: true });
        setBpmnXML(_xml.xml);
      } catch (error) {
        console.log('saveXML error', error)
      }
    };
  };

  useEffect(() => {
    if (searchProcessId) {
      getProcessInfo(searchProcessId);
    };
  }, [searchProcessId]);

  useEffect(() => {
    if (customModeler && processData) {
      const Canvas = customModeler.get('canvas');
      const Modeling = customModeler.get('modeling');
      // const EventBus = customModeler.get('eventBus');
      const ElementRegistry = customModeler.get('elementRegistry');
      const CommandStack = customModeler.get('commandStack');
      const CustomRenderer = customModeler.get('customRenderer');
      const CopyPaste = customModeler.get('copyPaste');
      const allInitElement = ElementRegistry.getAll();
      Canvas.addMarker(startEventId, 'startEvent-marker');
      Canvas.addMarker(firstTaskId, 'firstTask-marker');
      Canvas.addMarker(firstTaskId, 'element-created');
      if (allInitElement?.length > 0) {
        allInitElement.forEach((element: any) => {
          if (element?.type == 'bpmn:UserTask' && element?.id != firstTaskId || element?.type == 'bpmn:CallActivity') {
            Canvas.addMarker(element?.id, 'element-created');
            Canvas.addMarker(element?.id, 'userTask-marker');
            const businessObject = element?.businessObject;
            if (businessObject['loopCharacteristics']?.isSequential === false) {
              businessObject['loopCharacteristics'].isSequential = 'false';
            };
            if (businessObject['loopCharacteristics']?.isSequential === true) {
              businessObject['loopCharacteristics'].isSequential = 'true';
            };
          } else if (element?.type == 'bpmn:InclusiveGateway' || element?.type == 'bpmn:ParallelGateway' || element?.type == 'bpmn:ExclusiveGateway') {
            Canvas.addMarker(element?.id, 'element-created');
            Canvas.addMarker(element?.id, 'gateway-marker');
          } else if (element?.type == 'bpmn:EndEvent') {
            Canvas.addMarker(element?.id, 'element-created');
            Canvas.addMarker(element?.id, 'endEvent-marker');
          };
          Modeling.updateProperties(element, {});
        });
      };
      const rootElement = Canvas.getRootElement();
      if (processData?.processKey) {
        Modeling.updateProperties(rootElement, {
          id: processData?.processKey || '',
          name: processData?.name || '',
        });
      };
      saveDiagram();
      setAfterInit(true);
      CommandStack.clear();
      let copyElements: any[] = [];
      // const EventBus = customModeler.get('eventBus');
      // for (const k in EventBus._listeners) {
      //   customModeler?.on(k, (event: any) => {
      //     console.log(k, event);
      //   });
      // }

      // 画布右键无效
      let _canvas = document.getElementById('bpmnCanvas');
      if (_canvas) {
        _canvas.addEventListener('contextmenu', (ev) => {
          ev.preventDefault();
        });
      };

      // 搜索框滚动不触发画布缩放
      const searchResults = document?.getElementsByClassName('djs-search-results')?.[0];
      if (searchResults) {
        searchResults.addEventListener('wheel', (e) => {
          e.stopPropagation();
        });
      };

      // 设计器界面切换tab时关闭属性面板
      document?.addEventListener('click', (e: any) => {
        if (e?.target?.classList?.value?.indexOf('inbiz-studioPanel-switchButton') != -1) {
          setPropertiesPanelVisiable(false);
          setVariableDrawerVisiable(false);
          setVersionVisiable(false);
          setCurElement([]);
          let _selectedElements = customModeler.get('selection')._selectedElements;
          _selectedElements.forEach((item: any) => {
            customModeler.get('selection').deselect(item);
          });
        };
      });

      customModeler?.on('connection.added', (event: any) => {
        if (event) {
          setTimeout(() => {
            const _id = 'flow-' + uid(32).toUpperCase();
            Modeling.updateProperties(event?.element, { id: _id });
            const _sequenceFlowData = [...customPropertiesPanelRef?.current?.sequenceFlowData, {
              activityId: _id,
              remark: '',
              conditionExpression: [],
            }];
            customPropertiesPanelRef?.current?.setSequenceFlowData(_sequenceFlowData);
          }, 0);
        }
      });

      customModeler?.on('connection.removed', (event: any) => {
        const id = event?.element?.id;
        const _sequenceFlowData = customPropertiesPanelRef?.current?.sequenceFlowData?.filter((item: { activityId: string; }) => item?.activityId != id);
        customPropertiesPanelRef?.current?.setSequenceFlowData(_sequenceFlowData);
      });

      customModeler.on('selection.changed', (event: any) => {
        if (!_.isEmpty(event.oldSelection)) {
          event.oldSelection?.map((item: { type: string; }) => {
            if (item?.type == 'bpmn:SequenceFlow') {
              try {
                Modeling?.setColor(item, {
                  stroke: '#bbb',
                });
              } catch (error) { }
            }
          })
        }
        if (_.isEmpty(event.newSelection)) {
          // const allSequenceFlows = ElementRegistry?.filter((item: { type: string; }) => item?.type == 'bpmn:SequenceFlow')
          setCurElement([]);
          // allSequenceFlows?.map((item: any) => {
          //   try {
          //     Modeling?.setColor(item, {
          //       stroke: '#bbb',
          //     });
          //   } catch (error) { }
          // })
        } else {
          event.newSelection?.map((item: { type: string; id: string }) => {
            if (item?.type == 'bpmn:SequenceFlow') {
              try {
                Modeling?.setColor(item, {
                  stroke: '#1989fa',
                });
              } catch (error) { }
            }
          })
          setCurElement([...event.newSelection]);
        }
      });

      customModeler?.on('commandStack.changed', (event: any) => {
        saveDiagram();
      });

      customModeler?.on('connectionSegment.move.start', (event: any) => {
        Modeling.setColor(event?.connection, {
          stroke: '#1989fa',
        });
      });

      customModeler?.on('connectionSegment.move.end', (event: any) => {
        Modeling.setColor(event?.connection, {
          stroke: '#bbb',
        });
      });

      customModeler?.on('copyPaste.copyElement', (event: any) => {
        let canCopyElement = event?.elements?.filter(canOperate1);
        if (!_.isEmpty(canCopyElement)) {
          copyElements = canCopyElement;
        } else {
          setTimeout(() => {
            CopyPaste._clipboard.clear();
            copyElements = [];
          }, 0);
        };
      });

      customModeler?.on('commandStack.elements.create.postExecuted', (event: any) => {
        let _elements = event?.context?.elements;
        _elements = _elements?.filter((item: { type: string }) => item?.type == 'bpmn:UserTask' || item?.type == 'bpmn:CallActivity' || item?.type == 'bpmn:ExclusiveGateway' || item?.type == 'bpmn:ParallelGateway' || item?.type == 'bpmn:InclusiveGateway');
        const _copyElements = copyElements?.filter((item: { type: string }) => item?.type == 'bpmn:UserTask' || item?.type == 'bpmn:CallActivity' || item?.type == 'bpmn:ExclusiveGateway' || item?.type == 'bpmn:ParallelGateway' || item?.type == 'bpmn:InclusiveGateway');
        if (_copyElements?.length > 0 && CustomRenderer?.shearCache?.length == 0) {
          let _userTaskData: any[] = [...customPropertiesPanelRef?.current?.userTaskData];
          let _subProcessData: any[] = [...customPropertiesPanelRef?.current?.subProcessData];
          _elements?.forEach((element: any, index: number) => {
            let _businessObject = {
              ..._copyElements?.[index]?.businessObject,
              ..._copyElements?.[index]?.businessObject?.$attrs,
            };
            const _id = _copyElements?.[index]?.id;
            const _type = _copyElements?.[index]?.type;
            const id = _id?.split('-')?.[0] + '-' + uid(32).toUpperCase();
            _businessObject['id'] = id;
            switch (_type) {
              case 'bpmn:UserTask':
                if (_id?.startsWith('task')) {
                  _userTaskData.push({
                    ...customPropertiesPanelRef?.current?.userTaskData?.find((item: { activityId: string; }) => item?.activityId == _id),
                    activityId: id,
                  })
                } else if (_id?.startsWith('ctSign')) {
                  _userTaskData.push({
                    ...customPropertiesPanelRef?.current?.userTaskData?.find((item: { activityId: string; }) => item?.activityId == _id),
                    activityId: id,
                  })
                };
                break;
              case 'bpmn:CallActivity':
                _subProcessData.push({
                  ...customPropertiesPanelRef?.current?.subProcessData?.find((item: { activityId: string; }) => item?.activityId == _id),
                  activityId: id,
                });
                break;
              default:
                break;
            };
            Modeling.updateProperties(element, _businessObject);
          });
          customPropertiesPanelRef?.current?.setUserTaskData(_userTaskData);
          customPropertiesPanelRef?.current?.setSubProcessData(_subProcessData);
          copyElements = [];
          CustomRenderer.shearCache = [];
        };
      });

      customModeler?.on('commandStack.elements.delete.postExecuted', (event: any) => {
        setPropertiesPanelVisiable(false)
      });

      customModeler?.on('shape.added', (event: any) => {
        let _type = event?.element?.type;
        let _id = event?.element?.id;
        let _name = event?.element?.businessObject?.name;
        Canvas.addMarker(_id, 'element-created');

        // 任务
        if (_type == 'bpmn:UserTask' || _type == 'bpmn:CallActivity') {
          Canvas.addMarker(_id, 'userTask-marker');
        };

        // 分支
        if (_type == 'bpmn:InclusiveGateway' || _type == 'bpmn:ParallelGateway' || _type == 'bpmn:ExclusiveGateway') {
          Canvas.addMarker(_id, 'gateway-marker');
        };

        // 结束
        if (_type == 'bpmn:EndEvent') {
          Canvas.addMarker(_id, 'endEvent-marker');
        };

        if (CustomRenderer?.shearCache?.length == 0) {
          switch (_type) {
            case 'bpmn:UserTask':
              if (_id?.startsWith('task')) {
                let _userTaskData = [...customPropertiesPanelRef?.current?.userTaskData, defaultNewUserTask(_id, _name)];
                customPropertiesPanelRef?.current?.setUserTaskData(_userTaskData);
              } else if (_id?.startsWith('ctSign')) {
                let _userTaskData = [...customPropertiesPanelRef?.current?.userTaskData, defaultCtSignTask(_id, _name)];
                customPropertiesPanelRef?.current?.setUserTaskData(_userTaskData);
              };
              break;
            case 'bpmn:CallActivity':
              let _subProcessData = [...customPropertiesPanelRef?.current?.subProcessData, defaultSubProcess(_id)];
              customPropertiesPanelRef?.current?.setSubProcessData(_subProcessData);
              break;
            default:
              break;
          };
        }
      });
    };

    return () => {
      if (customModeler) {
        const EventBus = customModeler.get('eventBus');
        EventBus._destroy();
      };
      document.removeEventListener('click', () => { });
    };
  }, [customModeler, customPropertiesPanelRef, processData]);

  useEffect(() => {
    if (curElement?.length > 0) {
      setVariableDrawerVisiable(false);
      setVersionVisiable(false);
    };
    if (curElement?.length == 1) {
      setPropertiesPanelVisiable(true);
      customPropertiesPanelRef?.current?.setCurElement(curElement?.[0]);
    };
    if (curElement?.length > 1) {
      setPropertiesPanelVisiable(false);
      customPropertiesPanelRef?.current?.setCurElement(null);
    };
  }, [curElement, customPropertiesPanelRef]);

  const closeVariable = () => {
    setVariableDrawerVisiable(false);
  };

  const closeVersion = () => {
    setVersionVisiable(false);
  };

  const getSaveParams = () => {
    const _processData = processData || {};
    const _bpmnXML = bpmnXML || '';
    const _customModeler = customModeler || window?.inbizBpmn || {};
    const ElementRegistry = _customModeler.get('elementRegistry');
    const allElementIds = ElementRegistry.getAll()?.map((item: { id: string; }) => item?.id);
    const processDefinition = customPropertiesPanelRef?.current?.processDefinition;
    let _userTaskData = customPropertiesPanelRef?.current?.userTaskData?.filter((item: { activityId: string; }) => allElementIds?.includes(item?.activityId));
    const callActivityTask = customPropertiesPanelRef?.current?.subProcessData?.filter((item: { activityId: string; }) => allElementIds?.includes(item?.activityId));
    const sequenceFlow = customPropertiesPanelRef?.current?.sequenceFlowData?.filter((item: { activityId: string; }) => allElementIds?.includes(item?.activityId));
    let fieldPerm: any[] = [];
    _userTaskData = _userTaskData?.map((item: any) => {
      const isCounterSignNode = item?.baseInfo?.hasOwnProperty('isSequential');
      if (item?.assembly && item?.assembly?.length > 0) {
        const itemFieldPerm = {
          activityId: item?.activityId,
          fields: item?.assembly,
        };
        fieldPerm?.push(itemFieldPerm);
      };
      if (item?.hasOwnProperty('assembly')) {
        delete item.assembly;
      };
      return {
        ...item,
        baseInfo: {
          ...item?.baseInfo,
          isCounterSignNode,
        },
      };
    });
    let _extPropJson = {
      processDefinition,
      userTask: _userTaskData,
      callActivityTask,
      sequenceFlow,
    };
    const params = {
      ..._processData,
      proLcon: processDefinition?.proLcon || '',
      groupId: processDefinition?.groupId || '',
      sort: _processData?.sort ?? 5,
      pageKey: _processData?.pageKey ?? 1,
      pcPageKey: _processData?.pcPageKey ?? '',
      name: processDefinition?.processName || '',
      extPropJson: JSON.stringify(_extPropJson),
      processDefinitionXml: _bpmnXML,
      fieldPerm,
    };
    return params;
  };

  const getReleaseParams = () => {
    const _processData = processData || { processKey: '' };
    return _processData?.processKey;
  };

  const openVer = () => {
    setVersionVisiable(true);
  };

  useImperativeHandle(ref, () => ({
    getReleaseParams,
    getSaveParams,
    openVer,
    setVariable: () => {
      setVariableDrawerVisiable(true);
    },
  }));

  return (
    <>
      {/* <Header
        ref={headerRef}
        bpmnXML={bpmnXML}
        processData={processData}
        customModeler={customModeler}
        customPropertiesPanelRef={customPropertiesPanelRef}
        setVariableDrawerVisiable={setVariableDrawerVisiable}
        setVersionVisiable={setVersionVisiable}
      /> */}
      <div className="process-designer-body" style={{ height: '100%' }}>
        <CustomPalette customModeler={customModeler} ref={customPaletteRef} />
        <div style={{ flex: 1 }}>
          <ToolBar
            current={current}
            setCurrent={setCurrent}
            curElement={curElement}
            customModeler={customModeler}
            customPaletteRef={customPaletteRef}
            customPropertiesPanelRef={customPropertiesPanelRef}
            setPropertiesPanelVisiable={setPropertiesPanelVisiable}
            setVariableDrawerVisiable={setVariableDrawerVisiable}
            setVersionVisiable={setVersionVisiable}
          />
          <div style={{ display: 'flex', height: 'calc(100% - 48px)', position: 'relative' }}>
            <Spin spinning={loading}>
              <div id="bpmnCanvas" className={`bpmn-container ${afterInit ? 'afterInit' : ''}`} style={{ display: (searchProcessId || processData) && !requestFailed ? 'block' : 'none' }}></div>
              {current == 'code' && <SourceCode bpmnXML={bpmnXML} />}
            </Spin>
          </div>
        </div>
      </div>
      {
        processData
        &&
        <CustomPropertiesPanel
          customModeler={customModeler}
          processData={processData}
          ref={customPropertiesPanelRef}
          initCurElement={curElement?.[0]}
          propertiesPanelVisiable={propertiesPanelVisiable}
          setPropertiesPanelVisiable={setPropertiesPanelVisiable}
          defaultValue={processData?.extPropJson ? JSON.parse(processData?.extPropJson) : null}
        />
      }
      {
        variableDrawerVisiable
        &&
        <SetVariableDrawer
          processId={processData?.id || ''}
          processKey={processData?.processKey || ''}
          appInfo={appInfo}
          isNew={processData?.formTypes == 2}
          pageOutId={processData?.association?.split(',')?.[1] || ''}
          drawerParams={{
            visible: variableDrawerVisiable,
            width: 550,
            footer: null,
            mask: false,
            maskClosable: false,
            destroyOnClose: true,
            closable: false,
            placement: 'right',
            extra: <CloseOutlined onClick={closeVariable} />,
            onCancel: closeVariable,
            className: 'process-designer-set-variable-drawer',
          }}
        />
      }
      {
        versionVisiable
        &&
        <VersionManagement
          processKey={processData?.processKey || ''}
          drawerParams={{
            visible: versionVisiable,
            width: 550,
            footer: null,
            mask: false,
            maskClosable: false,
            destroyOnClose: true,
            closable: false,
            placement: 'right',
            extra: <CloseOutlined onClick={closeVersion} />,
            onCancel: closeVersion,
            className: 'process-designer-version-management-wapper',
          }}
        />
      }
    </>
  );
}, { forwardRef: true });

export default Bpmn;